#ifndef B2D_GPUBUFFER_H
#define B2D_GPUBUFFER_H

#include "IGraphicsResource.h"
#include "IUncopyable.h"

NS_B2D_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Intended use for a GPU buffer.
////////////////////////////////////////////////////////////////////////////////////////////////////
enum class B2D_API BufferUsage
{
	STREAM_DRAW,
	STREAM_READ,
	STREAM_COPY,
	STATIC_DRAW,
	STATIC_READ,
	STATIC_COPY,
	DYNAMIC_DRAW,
	DYNAMIC_READ,
	DYNAMIC_COPY
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Locking mode for the buffer.
////////////////////////////////////////////////////////////////////////////////////////////////////
enum class B2D_API LockMode
{
	/// No writing will be done, just reading.
	READ_ONLY = 1,
	/// Only writting will take place
	WRITE_ONLY = 2,
	/// Both reading and writting will occur
	READ_WRITE = READ_ONLY | WRITE_ONLY,
	/// Indicates that your application will overwrite every location in the locked resource
	/// with a write only operation. Should only be used with resources indicated as dynamic.
	/// This flag can lead to big speed improvements as no stall occurs (the old one is thrown
	/// away once finished with). However be aware that if you do a lot of discards per frame
	/// the AGP memory usage will climb as you are always getting a new resource created. 
	WRITE_ONLY_DISCARD = 4 | WRITE_ONLY,
	/// Default mode
	DEFAULT = READ_WRITE
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Base class hardware buffers.
////////////////////////////////////////////////////////////////////////////////////////////////////
class B2D_API GPUBuffer
	: public IUncopyable
	, public IGraphicsResource
{
public:
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	GPUBuffer lock_guard. Automatically unlocks on destruction.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	class B2D_API lock_guard final
		: public IUncopyable
	{
	private:
		GPUBuffer& mBuffer;
	public:
		lock_guard(GPUBuffer& buffer, LockMode mode, void** dst, size_t offset, size_t size);
		~lock_guard(void);
	};

protected:
	/// Size of the buffer (in bytes)
	size_t mBufferSize;
	/// Intended use of the buffer
	BufferUsage mBufferUsage;

	/// Flag to determine if the buffer is locked
	bool mIsLocked;

public:
	GPUBuffer(void);
	virtual ~GPUBuffer(void);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets the API specific handle.
	///
	/// \return	The handle.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void* getHandle(void) const = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Query if this object is initialised.
	///
	/// \return	true if initialised, false if not.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool isInitialised(void) const = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Query if this object is locked.
	///
	/// \return	true if locked, false if not.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	bool isLocked(void) const { return mIsLocked; }

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets the usage.
	///
	/// \return	The usage.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	BufferUsage getUsage(void) const { return mBufferUsage; }
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets buffer size.
	///
	/// \return	The buffer size.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	size_t getBufferSize(void) const { return mBufferSize; }

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Lock the buffer for writting/reading.
	/// 		To read the whole buffer set size to either 0 or the size of the buffer.
	/// 		The data can be read/writting to the pointer returned in dst.
	///
	/// \param	mode	   	The locking mode.
	/// \param [in,out]	dst	If destination for the buffer data.
	/// \param	offset	   	The offset into the buffer.
	/// \param	size	   	The size to lock.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void lock(LockMode mode, void** dst, size_t offset, size_t size);
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Unlocks this object.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	void unlock(void);


private:
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Implementation of locking.
	///
	/// \param	mode	   	The locking mode.
	/// \param [in,out]	dst	If destination for the buffer data.
	/// \param	offset	   	The offset into the buffer.
	/// \param	size	   	The size to lock.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void lockImpl(LockMode mode, void** dst, size_t offset, size_t size) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Implementation of unlocking.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void unlockImpl(void) = 0;
};

NS_B2D_END

#endif